// copyright 1993 Michael B. Johnson; some portions copyright 1994, MIT
// see COPYRIGHT for reuse legalities
//

#import "RIBHyperboloid.h"
#import <stdio.h>


@implementation RIBHyperboloid


+ initialize { return [RIBHyperboloid setVersion:1], self; }

- (BOOL)hasBoundingBox { return YES; }

- init
{
  [super init];

  point1[0] = 0.0;
  point1[1] = 0.0;
  point1[2] = 0.0;
  point2[0] = 0.5;
  point2[1] = 0.0;
  point2[2] = 0.0;
  
  return self;
}


- setPoint1:(RtPoint)newPoint1 point2:(RtPoint)newPoint2 
     thetaMax:(RtFloat)newThetaMax
     n:(int)newN tokens:(RtToken *)newTokens parms:(RtPointer *)newParms archiveVector:(char **)newArchiveVector
     printfTypeVector:(int *)newPrintfTypeVector printfNVector:(int *)newPrintfNVector
{  
   point1[0] = newPoint1[0];
   point1[1] = newPoint1[1];
   point1[2] = newPoint1[2];

   point2[0] = newPoint2[0];
   point2[1] = newPoint2[1];
   point2[2] = newPoint2[2];

   thetaMax = newThetaMax;
  
   [self setN:newN tokens:newTokens parms:newParms archiveVector:newArchiveVector printfTypeVector:newPrintfTypeVector printfNVector:newPrintfNVector];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setPoint1:(RtPoint)newPoint1
{
   point1[0] = newPoint1[0];
   point1[1] = newPoint1[1];
   point1[2] = newPoint1[2];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- setPoint2:(RtPoint)newPoint2
{
   point2[0] = newPoint2[0];
   point2[1] = newPoint2[1];
   point2[2] = newPoint2[2];

   dirtyBoundingBox = TRUE;
  
   return self;
}

- (RtPoint *)point1 { return &point1; }
- (RtPoint *)point2 { return &point2; }


#define	wwMax(a,b)  (((a)>(b))?(a):(b))
#define	wwMin(a,b)  (((a)>(b))?(b):(a))

- calculateBoundingBoxStartingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  boundingBox[0] = wwMin(point1[0], point2[0]);
  boundingBox[1] = wwMax(point1[0], point2[0]);

  boundingBox[2] = wwMin(point1[1], point2[1]);
  boundingBox[3] = wwMax(point1[1], point2[1]);

  boundingBox[4] = wwMin(point1[2], point2[2]);
  boundingBox[5] = wwMax(point1[2], point2[2]);
  dirtyBoundingBox = FALSE; 

  return self;
}

- (BOOL)isMoot
{
  if ((point1[0] == point2[0]) && (point1[1] == point2[1]) && (point1[2] == point2[2]))
  {  return YES;
  }
  return [super isMoot];
}

- (BOOL)theSameAs:otherRIBCommand
{
  RtPoint *otherPt1Ptr, *otherPt2Ptr;


  otherPt1Ptr = [otherRIBCommand point1];
  if (point1[0] != *otherPt1Ptr[0])
  {  return NO;
  }
  if (point1[1] != *otherPt1Ptr[1])
  {  return NO;
  }
  if (point1[2] != *otherPt1Ptr[2])
  {  return NO;
  }

  otherPt2Ptr = [otherRIBCommand point2];
  if (point2[0] != *otherPt2Ptr[0])
  {  return NO;
  }
  if (point2[1] != *otherPt2Ptr[1])
  {  return NO;
  }
  if (point2[2] != *otherPt2Ptr[2])
  {  return NO;
  }

  return [super theSameAs:otherRIBCommand];
}

- renderSelf:(WW3DCamera *)camera startingAt:(RtFloat)shutterOpenTime endingAt:(RtFloat)shutterCloseTime
{
  RiHyperboloidV(point1, point2, thetaMax, n, tokens, parms);

  return self;
}

- (unsigned long int)maxSampleBandwidth {  return ([super maxSampleBandwidth] + (unsigned long int)((3 + 3 + 1)  * sizeof(RtFloat))); }

- (BOOL)isLerpable { return YES; }

// note: because we've made the WWSampleList "safe" for having
// multiple samples with the same data, it's perfectly valid to return
// yourself or b
- lerpWith:b by:(float)uValue
{
   id      newMe = nil;
   RtFloat thetaMaxA, thetaMaxB;
   RtPoint *point1A, *point2A, newPoint;
   RtPoint *point1B, *point2B;


   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   newMe = [super lerpWith:b by:uValue]; // this makes a copy for us

   // okay, now do the specific stuff for this class
   point1A = [self point1];
   point1B = [b point1];
   newPoint[0] = (*point1A)[0] + (((*point1B)[0] - (*point1A)[0]) * uValue);
   newPoint[1] = (*point1A)[1] + (((*point1B)[1] - (*point1A)[1]) * uValue);
   newPoint[2] = (*point1A)[2] + (((*point1B)[2] - (*point1A)[2]) * uValue);
   [newMe setPoint1:newPoint]; 

   point2A = [self point2];
   point2B = [b point2];
   newPoint[0] = (*point2A)[0] + (((*point2B)[0] - (*point2A)[0]) * uValue);
   newPoint[1] = (*point2A)[1] + (((*point2B)[1] - (*point2A)[1]) * uValue);
   newPoint[2] = (*point2A)[2] + (((*point2B)[2] - (*point2A)[2]) * uValue);
   [newMe setPoint2:newPoint]; 

   thetaMaxA = [self thetaMax];
   thetaMaxB = [b thetaMax];
   [newMe setThetaMax:(thetaMaxA + ((thetaMaxB - thetaMaxA) * uValue))];
 
   return newMe;
}


- lerpSelfWith:b by:(float)uValue
{
   RtFloat thetaMaxA, thetaMaxB;
   RtPoint *point1A, *point2A, newPoint;
   RtPoint *point1B, *point2B;


   if (([self class] != [b class]) || (uValue <= 0.0))
   {  return self;
   }

   if (uValue >= 1.0)
   {  return b;
   }

   [super lerpSelfWith:b by:uValue]; // this makes a copy for us

   // okay, now do the specific stuff for this class
   point1A = [self point1];
   point1B = [b point1];
   newPoint[0] = (*point1A)[0] + (((*point1B)[0] - (*point1A)[0]) * uValue);
   newPoint[1] = (*point1A)[1] + (((*point1B)[1] - (*point1A)[1]) * uValue);
   newPoint[2] = (*point1A)[2] + (((*point1B)[2] - (*point1A)[2]) * uValue);
   [self setPoint1:newPoint]; 

   point2A = [self point2];
   point2B = [b point2];
   newPoint[0] = (*point2A)[0] + (((*point2B)[0] - (*point2A)[0]) * uValue);
   newPoint[1] = (*point2A)[1] + (((*point2B)[1] - (*point2A)[1]) * uValue);
   newPoint[2] = (*point2A)[2] + (((*point2B)[2] - (*point2A)[2]) * uValue);
   [self setPoint2:newPoint]; 

   thetaMaxA = [self thetaMax];
   thetaMaxB = [b thetaMax];
   [self setThetaMax:(thetaMaxA + ((thetaMaxB - thetaMaxA) * uValue))];

   return self;
}

- writeEve:(NXStream *)stream atTabLevel:(int)tab
{
   int  i;


   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Hyperboloid %f %f %f %f %f %f %f ", point1[0], point1[1], point1[2], point2[0], point2[1], point2[2], thetaMax);
   [super writeParameterList:stream];
   return self;
}

- writeInventorAtTime:(float)currentTime to:(NXStream *)stream atTabLevel:(int)tab
{
   RtFloat umax;
   RtFloat uknots[12];
   RtFloat uk[12] = {0.0, 0.0, 0.0, 0.25, 0.25, 0.5, 0.5, 0.75, 0.75, 1.0, 1.0, 1.0};
   RtFloat vknots[4] = {0.0, 0.0, 1.0, 1.0};
   RtFloat xuspline[9][2] = {{1.0, 0.0}, {M_SQRT1_2, -M_SQRT1_2}, {0.0, -1.0}, {-M_SQRT1_2, -M_SQRT1_2}, 
                             {-1.0, 0.0}, {-M_SQRT1_2, M_SQRT1_2},  {0.0, 1.0}, {M_SQRT1_2, M_SQRT1_2}, {1.0, 0.0}};
   RtFloat yuspline[9][2] = {{0.0, 1.0}, {M_SQRT1_2, M_SQRT1_2}, {1.0, 0.0}, {M_SQRT1_2, -M_SQRT1_2}, {0.0, -1.0}, 
  	                     {-M_SQRT1_2, -M_SQRT1_2}, {-1.0, 0.0}, {-M_SQRT1_2, M_SQRT1_2}, {0.0, 1.0}};
   RtFloat zwuspline[9] = {1.0, M_SQRT1_2, 1.0, M_SQRT1_2, 1.0, M_SQRT1_2, 1.0, M_SQRT1_2, 1.0};
   int i, j, k;


   umax = [self thetaMax] / 360.0;
   for (i=0; i<12; i++)
   {  uknots[i] = uk[i] / umax;
   }

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "# ");
   [self writeEve:stream atTabLevel:0];
   NXPrintf(stream, "\n");

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "# since Inventor doesn't directly support quadrics in any terribly useful way (sigh), we turn it into a NURBS surface:\n");

   for (i = 0; i < tab; i++)
   {  NXPrintf(stream, "\t");
   }
   NXPrintf(stream, "Separator {\n");

   for (i = 0; i < (tab+1); i++)  {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "Coordinate4 {\n");
   for (i = 0; i < (tab+2); i++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "point [");

   for (j=0; j<9; j++)
   {  if (!j)
      {  NXPrintf(stream, 
		  "%f %f %f %f,\n", 
		  (point1[0] * xuspline[j][0] + point1[1] * xuspline[j][1]),
		  (point1[0] * yuspline[j][0] + point1[1] * yuspline[j][1]),
		  (point1[2] * zwuspline[j]),
		  (zwuspline[j]));
      }
      else
      {  NXPrintf(stream, 
		  "       %f %f %f %f,\n", 
		  (point1[0] * xuspline[j][0] + point1[1] * xuspline[j][1]),
		  (point1[0] * yuspline[j][0] + point1[1] * yuspline[j][1]),
		  (point1[2] * zwuspline[j]),
		  (zwuspline[j]));
      }
      for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   }

   for (j=0; j<8; j++)
   {  NXPrintf(stream, 
	       "       %f %f %f %f,\n", 
	       (point2[0] * xuspline[j][0] + point2[1] * xuspline[j][1]),
	       (point2[0] * yuspline[j][0] + point2[1] * yuspline[j][1]),
	       (point2[2] * zwuspline[j]),
	       (zwuspline[j]));
      for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   }
   NXPrintf(stream, 
	    "       %f %f %f %f]\n", 
	    (point2[0] * xuspline[j][0] + point2[1] * xuspline[j][1]),
	    (point2[0] * yuspline[j][0] + point2[1] * yuspline[j][1]),
	    (point2[2] * zwuspline[j]),
	    (zwuspline[j]));

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "NurbsSurface {\n");
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numUControlPoints 9\n");
   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "numVControlPoints 2\n");

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "uKnotVector [");
   for (i=0; i<11; i++)
   {	NXPrintf(stream, "%f, ", uknots[i]);
   }
   NXPrintf(stream, "%f]\n", uknots[i]);

   for (k = 0; k < (tab+2); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "vKnotVector [");
   for (i=0; i<3; i++)
   {	NXPrintf(stream, "%f, ", vknots[i]);
   }
   NXPrintf(stream, "%f]\n", vknots[i]);
   for (k = 0; k < (tab+1); k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");
   for (k = 0; k < tab; k++) {  NXPrintf(stream, "\t"); }
   NXPrintf(stream, "}\n");

   return self;
}


- read:(NXTypedStream*)stream 
{
    int version;
    [super read:stream];

    version = NXTypedStreamClassVersion(stream,"RIBHyperboloid");
    if (version == 0) NXReadTypes(stream,"i",&version), version=1;
    if (version == 1) {
	NXReadArray(stream, "f", 3, point1);
	NXReadArray(stream, "f", 3, point2);
    } else {
    }
    return self;
}

- write:(NXTypedStream*)stream 
{
    [super write:stream];
    NXWriteArray(stream, "f", 3, point1);
    NXWriteArray(stream, "f", 3, point2);
    return self;
}


@end
